package com.dianrui.firemenmonitor.util;

/**
 * 通过自定义算法代码来纠偏地图由GPS定位到的经纬度在地图上显示或解析位置时出现偏移的问题
 * 在中国内地主流的在线地图服务和接口，应国家相关部门的要求，在发布地图是都进行了国家国测局给的不可逆的加密算法（不公开的算法）进行无规律的GCJ-02（俗称：火星坐标系）坐标偏移，大概与实际的WGS84坐标偏移几十米到几百米之间。现在的在线地图主要的坐标系有WGS84、GCJ-02、BD-09三种，WGS84原始坐标，未经过偏移，大部分GPS设备和矢量数据都采用这种坐标系。在线地图中Google地图（国外）和天地图在线地图未经过偏移。GCJ-02坐标：高德、腾讯以及Google地图（中国版）在线地图采用这种坐标，BD-09是百度地图独有的坐标系，在GCJ-02基础上做了二次偏移。
 三种类型的地图坐标系的简介：
 1、WGS－84原始坐标系
 一般用国际GPS纪录仪记录下来的经纬度，通过GPS定位拿到的原始经纬度，Google和高德地图定位的的经纬度（国外）都是基于WGS－84坐标系的；但是在国内是不允许直接用WGS84坐标系标注的，必须经过加密后才能使用；
 2、GCJ－02坐标系
 GCJ－02坐标系又名“火星坐标系”，是我国国测局独创的坐标体系，由WGS－84加密而成，在中国内地，必须至少使用GCJ－02坐标系，或者使用在GCJ－02加密后再进行加密的坐标系，如百度坐标系。高德和Google地图（中国版）在中国内地都是使用GCJ－02坐标系，可以说，GCJ－02是中国内地最广泛使用的坐标系；
 3、百度坐标系:bd-09
 百度坐标系是在GCJ－02坐标系的基础上再次加密偏移后形成的坐标系，只适用于百度地图。

 为什么会发生偏移？
 1、由于坐标系之间不兼容，如在百度地图上定位的经纬度拿到高德地图上直接描点就肯定会发生偏移；只考虑中国内地的情况，高德地图和Google地图（中国版）是可以不经过转换也能够准确显示的（在中国内地用的都是GCJ－02坐标系）；
 2、在中国内地定位的经纬度，然后在国外网络下显示也会发生偏移（谷歌和高德会依据网络的情况选择使用WGS－84坐标还是GCJ－02坐标，百度地图则一直使用bd-02坐标系）
 3、定位方式，在Android上可以通过网络或GPS获取经纬度。通过地图SDK定位获取的经纬度，地图SDK会自动选择加密的方式（如Google地图会根据在中国内地和境外选择不同的坐标系）然后再将点显示在地图上，这个时候是没有偏移的；如果直接将经纬度在地图上显示，可能就会因为地域或网络的问题导致使用的坐标系不同，进而发生来偏移。
 下面是解决Android开发时定位到的经纬度在地图上显示或解析位置时出现偏移的问题的算法代码，此算法代码不是中国国测局的不可逆的加密算法代码，所以可能由WGS－84坐标转换后的GCJ－02坐标不是完全的吻合，不过误差减少到几米到十几米之间。
 */
public class MapFixUtil {

    final static double pi = 3.14159265358979324;
    final static double a = 6378245.0;
    final static double ee = 0.00669342162296594323;

    public static double[] transform(double wgLat, double wgLon) {
        double[] latlng = new double[2];
        if (outOfChina(wgLat, wgLon)) {
            latlng[0] = wgLat;
            latlng[1] = wgLon;
            return latlng;
        }
        double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
        double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
        double radLat = wgLat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        latlng[0] = wgLat + dLat;
        latlng[1] = wgLon + dLon;
        return latlng;
    }

    private static boolean outOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        return lat < 0.8293 || lat > 55.8271;
    }

    private static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    private static double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }
}
